import readline
import urllib
import urllib2
import simplejson
import sys
import base64
import re
import os
from pprint import pprint

# Copyright Neville Newey, Charl van Niekerk, Stii Pretorius

VERSION_STRING = 'v0.02'
DEBUG = False
STATUS_FORMAT = "%(text)s %(created_at)s"

n = 20 # Number of statii to print by default

# Twitter API Urls...
PUBLIC_TIMELINE_URL = 'http://twitter.com/statuses/public_timeline.json'
FRIENDS_TIMELINE_URL = 'http://twitter.com/statuses/friends_timeline/%s.json'
USER_TIMELINE_URL = 'http://twitter.com/statuses/user_timeline/%s.json'
UPDATE_URL = 'http://twitter.com/statuses/update.json'
FRIENDS_URL = 'http://twitter.com/statuses/friends/%s.json'
FOLLOWERS_URL = 'http://twitter.com/statuses/followers.json'
DIRECT_MESSAGES_URL = 'http://twitter.com/direct_messages.json'
DIRECT_MESSAGE_URL = 'http://twitter.com/direct_messages/new.json'
FRIENDSHIP_CREATE_URL = 'http://twitter.com/friendships/create/%s.json'
FRIENDSHIP_DESTROY_URL = 'http://twitter.com/friendships/destroy/%s.json'
DESTROY_URL = 'http://twitter.com/statuses/destroy/%s.json'
REPLIES_URL = 'http://twitter.com/statuses/replies.json'
FEATURED_URL = 'http://twitter.com/statuses/featured.json'
SHOW_URL = 'http://twitter.com/users/show/%s.json'

# Command Regexes
FRIENDS_TIMELINE_PAT = re.compile('''
    ^
    (friends_timeline|ft)
    (
      \s+
      (?P<screen_name>.*)    # Optional screen_name 
    )?
    $
    ''', re.VERBOSE | re.IGNORECASE)

USER_TIMELINE_PAT = re.compile('''
    ^
    (user_timeline|ut)
    (
      \s+
      (?P<screen_name>.*)    # Optional screen_name 
    )?
    $
    ''', re.VERBOSE | re.IGNORECASE)

UPDATE_PAT = re.compile('''
    ^
    (u|update|up)       # aliases for update command
    \s+                 # whitespace
    (?P<status>.*)      # the actual text for the status
    $
    ''', re.VERBOSE | re.IGNORECASE)

DIRECT_MESSAGE_PAT = re.compile('''
    ^
    (direct|new)        # alias for direct is new
    \s+                 # whitespace
    (?P<recipient>.*?)  # intended recipient
    \s+                 # white space
    (?P<message>.*)     # actual message
    $
    ''', re.VERBOSE | re.IGNORECASE)

FOLLOW_CMD_PAT = re.compile('''
    ^
    (follow|fc)         # alias for follow is fc
    \s+                 # whitespace
    (?P<friend_name>.*) # screen name of friend to follow
    $
    ''', re.VERBOSE | re.IGNORECASE)

UNFOLLOW_CMD_PAT = re.compile('''
    ^
    (unfollow|fd)         # alias for unfollow is fd
    \s+                 # whitespace
    (?P<friend_name>.*) # screen name of friend to unfollow
    $
    ''', re.VERBOSE | re.IGNORECASE)

DESTROY_CMD_PAT = re.compile('''
    ^
    (destroy)
    \s+
    (?P<message_id>\d+)
    $
    ''', re.VERBOSE | re.IGNORECASE)

FRIENDS_CMD_PAT = re.compile('''
    ^
    (friends|fr)
    (
      \s+
      (?P<screen_name>.*)
    )?   # optional screen_name
    $    
    ''', re.VERBOSE | re.IGNORECASE)


SHOW_CMD_PAT = re.compile('''
    ^
    (show|sh)
    (
        \s+
        (?P<screen_name>.*)
    )?  # optional screen_name
    $
    ''', re.VERBOSE | re.IGNORECASE)


reset = chr(27)+'[0m'
red = chr(27)+'[31m'
green = chr(27)+'[32m'
yellow = chr(27)+'[33m'
blue = chr(27)+'[34m'
magenta = chr(27)+'[35m'
cyan = chr(27)+'[36m'
bold_on = chr(27)+'[1m'
bold_off = chr(27)+'[22m'


def color_write(color, s):
    sys.stdout.write('%s%s%s' % (color, s, reset))


def get_twitter_response(url, username=None, password=None):
    txdata = None
    txheaders = {}
    req = urllib2.Request(url, txdata, txheaders)
    opener = urllib2.build_opener()
    useragent = 'Twish v0.01'
    opener.addheaders = [('User-agent', useragent)]
    if username and password:
        base64string = base64.encodestring('%s:%s' % (username, password))[:-1]
        opener.addheaders.append(("Authorization", "Basic %s" % base64string))
    data = opener.open(req).read()
    return data

def get_json_response(url, username=None, password=None):
    r = get_twitter_response(url, username, password)
    if DEBUG:
        print r
    try:
        r_ = simplejson.loads(r)
        return r_
    except:
        print 'Failed to interpret JSON response for %s' % url

def print_statuses(statuses, ids=False):
    for s in statuses[0:n]:
        if not s:
            continue
        screen_name = s['user']['screen_name']
        color_write(magenta, '%s: ' % screen_name)
        if ids:
            print '%s [%s%s%s]' % (s['text'], yellow, s['id'], reset)
        else:
            #print '%s %s%s%s' % (s['text'], yellow, s.get('created_at',''), reset)
            print STATUS_FORMAT % s

def print_public_timeline():
    r = get_json_response(PUBLIC_TIMELINE_URL)
    print_statuses(r)
    
def print_friends_timeline(username, password, screen_name=None):
    if screen_name:
        url = FRIENDS_TIMELINE_URL % screen_name
    else:
        url = FRIENDS_TIMELINE_URL % username
    r = get_json_response(url, username, password)
    print_statuses(r)

def print_user_timeline(username, password, screen_name=None):
    if screen_name:
        url = USER_TIMELINE_URL % screen_name
    else:
        url = USER_TIMELINE_URL % username
    r = get_json_response(url, username, password)
    print_statuses(r, ids=True)


def update(status, username, password):
    values = dict(status=status[0:160])
    data = urllib.urlencode(values)
    request = urllib2.Request(UPDATE_URL, data)
    base64string = base64.encodestring('%s:%s' % (username, password))[:-1]
    request.add_header("Authorization", "Basic %s" % base64string)
    r = urllib2.urlopen(request).read()
    r = simplejson.loads(r)
    print_statuses([r])

def direct_message(recipient, message, username, password):
    values = dict(user=recipient, text=message[0:140])
    data = urllib.urlencode(values)
    request = urllib2.Request(DIRECT_MESSAGE_URL, data)
    base64string = base64.encodestring('%s:%s' % (username, password))[:-1]
    request.add_header("Authorization", "Basic %s" % base64string)
    print 'Sending message directly to %s%s%s' % (cyan, recipient, reset)
    try:
        r = urllib2.urlopen(request).read()
        r = simplejson.loads(r)
    except:
        print 'Error sending direct message to %s%s%s.' %  \
            (cyan, recipient, reset)
        print '(Maybe you mispelled the recipients name or the recipient is not your friend.)'
        
def print_friends(username, password, screen_name=None):
    if screen_name:
        url = FRIENDS_URL % screen_name
    else:
        url = FRIENDS_URL % username
    friends = get_json_response(url, username, password)
    screen_names = [f['screen_name'] for f in friends]
    screen_names.sort()
    screen_names = ['%s%s%s' % (cyan, s, reset) for s in screen_names]
    if screen_name:
        print '%s has %s friends:' % (screen_name, len(screen_names))
    else:
        print 'You have %s friends:' % len(screen_names)
    print ', '.join(screen_names)

def print_user_info(username, password, screen_name=None):
    if screen_name:
        url = SHOW_URL % screen_name
    else:
        url = SHOW_URL % username
    info = get_json_response(url, username, password)
    keys = info.keys()
    keys.sort()
    for k in keys:
        print '%s%25s%s:%s' % (magenta, k, reset, info[k])
    #pprint(info)

    
def print_followers(username, password):
    followers = get_json_response(FOLLOWERS_URL, username, password)
    screen_names = [f['screen_name'] for f in followers]
    screen_names.sort()
    screen_names = ['%s%s%s' % (cyan, s, reset) for s in screen_names]
    print 'You have %s followers:' % len(screen_names)
    print ', '.join(screen_names)
    
def print_direct_messages(username, password):
    r = get_json_response(DIRECT_MESSAGES_URL, username, password)
    for m in r:
        color_write(cyan,  m['sender']['screen_name'])
        print ': %s' % m['text']
    #print_statuses(r)

def print_replies(username, password):
    r = get_json_response(REPLIES_URL, username, password)
    for m in r:
        color_write(cyan, m['user']['screen_name'])
        print ':%s' % m['text']


def friendship_create(friend_name, username, password):
    url = FRIENDSHIP_CREATE_URL % friend_name
    try:
        r = get_json_response(url, username, password)
        print 'You are now following %s%s%s.' % (cyan, friend_name, reset)
    except:
        print 'Error while adding friend %s%s%s. ' % (cyan, friend_name, reset) 

def friendship_destroy(friend_name, username, password):
    url = FRIENDSHIP_DESTROY_URL % friend_name
    try:
        r = get_json_response(url, username, password)
        print 'You are now no longer following %s%s%s ' % (cyan, friend_name, reset)
    except:
        print 'Error while deleting friend %s%s%s. ' % (cyan, friend_name, reset) 

def destroy(message_id, username, password):
    url = DESTROY_URL % message_id
    try:
        r = get_json_response(url, username, password)
        print 'Message %s destroyed.' % message_id
    except:
        print 'There was an error destroying message %s' % message_id

def print_featured(username, password):
    url = FEATURED_URL
    featured = get_json_response(url, username, password)
    screen_names = [f['screen_name'] for f in featured]
    screen_names.sort()
    screen_names = ['%s%s%s' % (cyan, s, reset) for s in screen_names]
    print ', '.join(screen_names)    


def print_help():
    print 'The following commands are currently supported:'
    print '''        %spublic%s(%spub%s) - public timeline''' % (green, reset, green, reset)
    print '''        %sfriends_timeline%s (%sft%s, "%s'%s") [screen_name] - friends timeline''' % (green, reset, green, reset, green, reset)
    print '''        %suser_timeline%s (%sut%s) [screen_name] - user timeline (defaults to your own)''' % (green, reset, green, reset)
    print '''        %sfriends%s (%sfr%s, %sf%s) [screen_name] - list friends''' % (green, reset, green, reset, green, reset)
    print '''        %sfollowers%s (%sfls%s) - list followers''' % (green, reset, green, reset)
    print '''        %sfollow%s (%sfc%s) screen_name - start following screen_name''' % (green, reset, green, reset)
    print '''        %sdirect%s friend_name msg - send msg directly to friend_name''' % (green, reset)
    print '''        %sdm%s - show my received direct messages''' % (green, reset)
    print '''        %sdestroy%s message_id - destroys (deletes) the message with id message_id''' % (green, reset)
    print '''        %sreplies%s - replies from others to you''' % (green, reset)
    print '''        %sfeatured%s - list featured users''' % (green, reset)
    print '''        %sshow%s (%ssh%s) - show [screen_name] - detailed info about a user''' % (green, reset, green, reset)
    print ''' *** This help text is incomplete. Look in the source for further commands.'''

def get_config():
    try:
        os.stat('.twish.rc')
        rcfile = open('.twish.rc')
        lines = rcfile.readlines()
        lines = [l.strip() for l in lines]
        all_lines = ''.join(lines)
        config = eval(all_lines)
        return config
    except:
        return {}

if __name__ == '__main__':
    print 'Welcome to Twish %s' % VERSION_STRING
    config = get_config()
    username = config.get('username') or raw_input('Please enter your twitter username: ')
    password = config.get('password') or raw_input('Please enter your twitter password: ')
    print 'Type "help" followed by the enter key for commands.'
    while True:
        cmd = raw_input('%s%s%s > ' % (yellow, username, reset))
        if cmd == 'quit':
            break
        elif cmd == '':
            pass
        elif cmd == 'pub' or cmd == 'public':
            print_public_timeline()
        elif cmd in ("'", 'ft', 'friends_timeline'):
            print_friends_timeline(username, password)
        elif cmd in ('ut', 'user_timeline'):
            print_user_timeline(username, password)
        elif cmd in ('f', 'fr', 'friends'):
            print_friends(username, password)
        elif cmd in ('fls', 'followers'):
            print_followers(username, password)
        elif cmd in ('dm', 'direct_messages'):
            print_direct_messages(username, password)
        elif cmd ==  'help':
            print_help()
        elif cmd in ('r', 'rep', 'replies'):
            print_replies(username, password)
        elif cmd == 'featured':
            print_featured(username, password)
        else:
            m = UPDATE_PAT.match(cmd)
            if m:
                status = m.group('status')
                if status:
                    update(status, username, password)
                continue
            m = DIRECT_MESSAGE_PAT.match(cmd)
            if m:
                recipient = m.group('recipient')
                message = m.group('message')
                if recipient and message:
                    direct_message(recipient, message, username, password)
                continue
            m = FOLLOW_CMD_PAT.match(cmd)
            if m:
                friend_name = m.group('friend_name')
                if friend_name:
                    friendship_create(friend_name, username, password)
                continue
            m = UNFOLLOW_CMD_PAT.match(cmd)
            if m:
                friend_name = m.group('friend_name')
                if friend_name:
                    friendship_destroy(friend_name, username, password)
                continue
            m = DESTROY_CMD_PAT.match(cmd)
            if m:
                message_id = m.group('message_id')
                destroy(message_id, username, password)
                continue
            m = FRIENDS_TIMELINE_PAT.match(cmd)
            if m:
                screen_name = m.group('screen_name')
                print_friends_timeline(username, password, 
                      screen_name=screen_name)
                continue
            m = USER_TIMELINE_PAT.match(cmd)
            if m:
                screen_name = m.group('screen_name')
                try:
                    print_user_timeline(username, password,
                        screen_name=screen_name)
                except urllib2.HTTPError:
                    print 'no such user exists: %s' % screen_name
                continue
            m = FRIENDS_CMD_PAT.match(cmd)
            if m:
                screen_name = m.group('screen_name')
                print_friends(username, password, screen_name=screen_name)
                continue
            m = SHOW_CMD_PAT.match(cmd)
            if m:
                screen_name = m.group('screen_name')
                print_user_info(username, password, screen_name=screen_name)
                continue
            print 'Command not recognized. Type %shelp%s to view commands.' % (yellow, reset) 
                    
                
